import Obj, {equalsTo} from "./Obj";
import {hash} from "./Hash";

/**
 * 两种空值。
 */
type nullType = undefined | null;

/**
 * 一个可以含有空值{@link #nullType}的对象。
 *
 * 由于所有的数据结构中都不支持null和undefined的存储，因此此包提供了这个类以解决该问题。
 *
 * @param E 需要存储在该类中的元素类型
 * @param N 空值的类型，可以是null，也可以是undefined
 * @version 1.0
 */
export default class Optional<E, N extends nullType> extends Obj {

  /**
   * 该元素当前的值。
   *
   * 这个值可能是空值undefined。
   *
   * @private
   * @version 1.0
   */
  public readonly value: E | N;

  /**
   * 构造此对象，并提供一个初始值或空值。
   *
   * @param value 需要存入此对象的初始值
   *
   * @protected
   * @version 1.0
   */
  public constructor(value: E | N) {
    super();
    this.value = value;
  }

  /**
   * 返回此对象中存储的值。
   *
   * @return {E | undefined} 此对象中存储的值或undefined若此对象没有存储值
   * @version 1.0
   */
  public get(): E | N {
    return this.value;
  }

  /**
   * 如果此对象中存储的值为null或undefined，则返回true，否则返回false。
   *
   * @return {boolean} 如果次对象中存储的值为null或undefined，则返回true，否则返回false。
   * @version 1.0
   */
  public isEmpty(): boolean {
    return this.value === undefined || this.value === null;
  }

  /**
   * 如果此对象中存储的值不为null或undefined，则返回true，否则返回false。
   *
   * @return {boolean} 如果次对象中存储的值不为null或undefined，则返回true，否则返回false。
   * @version 1.0
   */
  public isPresent(): boolean {
    return !this.isEmpty();
  }

  /**
   * 返回此对象中存储的值，若对象未存储值，则返回默认值。
   *
   * @param e 当对象未存储值时的默认返回值
   * @return {E} 返回此对象中存储的值，若对象未存储值，则返回默认值
   * @version 1.0
   */
  public orElse(e: E): E {
    return this.isEmpty() ? e : (this.value as E);
  }

  /**
   * 实现比较方法，通过比较两者的存储值以进行判断。
   *
   * @param b 需要被判断的另一个对象
   * @return {boolean} 如果两个对象互相相等，则返回true，否则返回false
   * @version 1.0
   */
  public equals(b: Obj): boolean {
    if (b instanceof Optional) {
      return equalsTo(this.value, b.value);
    }
    return false;
  }

  public hashCode(): number {
    return hash(this.value);
  }
}
